﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using System.Runtime.InteropServices;
using TestEmguCV.Objects;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using TestEmguCV.Recognizer;

namespace TestEmguCV
{
    public partial class fMain : Form
    {
        
        Capture webcam;
        CascadeClassifier haar;
        private Image<Bgr, Byte> imgOriginal;
        private Image<Gray, Byte> imgProcessed;

        Image<Gray, byte> result, TrainedFace = null;

        public List<People> ListPeople = new List<People>();
        public List<string> IDs = new List<string>();
        public List<string> NameImages = new List<string>();
        public List<Image<Gray, Byte>> TrainImages = new List<Image<Gray, byte>>();
        public int IDsCount = 0;
        public string id ="";

        public int CaptureCount { get; set; }

        public fMain()
        {
            InitializeComponent();
            string filename;
            if (File.Exists(Application.StartupPath + "/TrainedFaces/peoples.txt") && File.Exists(Application.StartupPath + "/TrainedFaces/ids.txt"))
            {
                readData();
                StreamReader sr = new StreamReader(Application.StartupPath + "/TrainedFaces/ids.txt");
                string[] line = sr.ReadLine().Split('#');
                IDsCount = int.Parse(line[0]);
                for (int i = 1; i <= IDsCount; i++)
                {
                    filename = "face" + i + ".bmp";
                    TrainImages.Add(new Image<Gray, byte>(Application.StartupPath + "/TrainedFaces/" + filename));
                    IDs.Add(line[i]);
                }
            }
            haar = new CascadeClassifier(Application.StartupPath + "/HaarCascade/haarcascade_frontalface_default.xml");
        }

        public void saveData()
        {
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream(Application.StartupPath + "/TrainedFaces/peoples.txt",
                         FileMode.Create,
                         FileAccess.Write, FileShare.None);
            formatter.Serialize(stream, ListPeople);
            stream.Close();
        }

        public void readData()
        {
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream(Application.StartupPath + "/TrainedFaces/peoples.txt",
                                      FileMode.Open,
                                      FileAccess.Read,
                                      FileShare.Read);
            ListPeople = (List<People>)formatter.Deserialize(stream);
            stream.Close();
        }

        private void processingImage(object sender, EventArgs e)
        {
            imgOriginal = webcam.QueryFrame().Resize(320, 240, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC);
            imgProcessed = imgOriginal.Convert<Gray, Byte>();

            Rectangle[] detector = haar.DetectMultiScale(imgProcessed, 1.1, -1, new Size(20, 20), Size.Empty);
            if (detector != null)
                foreach (var rec in detector)
                {
                    result = imgProcessed.Copy(rec).Resize(100, 100, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC);
                    imgOriginal.Draw(rec, new Bgr(Color.Red), 2);
                    if ( TrainImages.Count != 0)
                    {
                        //TermCriteria for face recognition with numbers of trained images like maxIteration
                        MCvTermCriteria termCrit = new MCvTermCriteria(IDsCount, 0.001);

                        //Eigen face recognizer
                        ObjectRecognizer recognizer = new ObjectRecognizer(
                           TrainImages.ToArray(),
                           IDs.ToArray(),
                           double.Parse(txtThres.Text),
                           termCrit);

                        id = recognizer.Recognize(result);
                    }
                }
            if (id != "" && ListPeople != null)
            {
                foreach (var i in ListPeople)
                {
                    if (i.ID == id)
                    {
                        txtInfo.Text = i.FirstName + " " + i.LastName;
                    }
                }
            }
            else
            {
                txtInfo.Text = "";
            }
            ibOriginal.Image = imgOriginal;
        }

        private void btnProcessed_Click(object sender, EventArgs e)
        {
            try
            {
                webcam = new Capture();
                Application.Idle += processingImage;
                btnStart.Enabled = false;
            }
            catch (NullReferenceException)
            {
                MessageBox.Show("Không thể mở webcam");
            }
        }

        private void bSpeak_Click(object sender, EventArgs e)
        {
            Application.Idle -= processingImage;
        }

        private void btnSubmit_Click(object sender, EventArgs e)
        {
            bool existP=false;
            if (ListPeople != null)
            {
                foreach (var p in ListPeople)
                {
                    if (txtID.Text == p.ID)
                    {
                        existP = true;
                        break;
                    }
                }
                if (!existP)
                {
                    People p = new People();
                    p.ID = txtID.Text;
                    p.LastName = txtLName.Text;
                    p.FirstName = txtFName.Text;
                    p.Birth = txtBDay.Text;
                    p.Address = txtAddr.Text;
                    p.Gender = txtGen.Text;
                    ListPeople.Add(p);
                    txtID.Enabled = false;
                    txtLName.Enabled = false;
                    txtFName.Enabled = false;
                    txtBDay.Enabled = false;
                    txtAddr.Enabled = false;
                    txtGen.Enabled = false;
                    btnSubmit.Enabled = false;
                    btnCapture.Enabled = true;
                    CaptureCount = 0;
                    saveData();
                }
                else
                {
                    MessageBox.Show("Trùng id");
                }
            }
        }

        private void btnCapture_Click(object sender, EventArgs e)
        {
            if (CaptureCount < 5)
            {
                try
                {
                    CaptureCount++;
                    //Trained face counter
                    IDsCount++;

                    //Get a gray frame from capture device
                    imgProcessed = webcam.QueryGrayFrame().Resize(320, 240, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC);

                    //Face Detector
                    Rectangle[] detector = haar.DetectMultiScale(imgProcessed, 1.2, 0, new Size(20, 20), Size.Empty);
                    if (detector != null)
                        foreach (var rec in detector)
                        {
                            imgOriginal.Draw(rec, new Bgr(Color.Red), 2);
                            break;
                        }

                    //resize face detected image for force to compare the same size with the 
                    //test image with cubic interpolation type method
                    TrainedFace = result.Resize(100, 100, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC);
                    TrainImages.Add(TrainedFace);
                    IDs.Add(txtID.Text);

                    //Show face added in gray scale
                    //imageBox1.Image = TrainedFace;

                    //Write the number of triained faces in a file text for further load
                    File.WriteAllText(Application.StartupPath + "/TrainedFaces/ids.txt", IDs.Count + "#");

                    //Write the labels of triained faces in a file text for further load
                    for (int i = 1; i <= IDs.Count; i++)
                    {
                        TrainImages[i-1].Save(Application.StartupPath + "/TrainedFaces/face" + i + ".bmp");
                        File.AppendAllText(Application.StartupPath + "/TrainedFaces/ids.txt", IDs[i - 1] + "#");
                    }
                    
                }
                catch
                {
                    MessageBox.Show("Enable the face detection first", "Training Fail", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
                //ilAvatar.Images.Clear();
                if (TrainImages.Count < 5)
                {
                    for (int i = 0; i < TrainImages.Count; i++)
                    {
                        if (IDs[i] == txtID.Text)
                        {
                            Console.WriteLine("Found img: " + IDs[i] + "\n");
                            ilAvatar.Images.Add(TrainImages[i].ToBitmap());
                        }
                    }
                }
                else
                {
                    for (int i = IDs.Count - 5; i < IDs.Count; i++)
                    {
                        if (IDs[i] == txtID.Text)
                        {
                            Console.WriteLine("Found img: " + IDs[i]+"\n");
                            ilAvatar.Images.Add(TrainImages[i].ToBitmap());
                        }
                    }
                }
                lvAvatar.View = View.LargeIcon;
            }
            else
            {
                CaptureCount = 0;
                btnCapture.Enabled = false;
                btnSubmit.Enabled = true;
                txtID.Enabled = true;
                txtLName.Enabled = true;
                txtFName.Enabled = true;
                txtBDay.Enabled = true;
                txtAddr.Enabled = true;
                txtGen.Enabled = true;
            }
        }
    }
}
